package com.link.base.base.autoconfig.nocode.service;

import com.link.base.base.autoconfig.autoconfig.model.FileExport;
import com.link.base.base.autoconfig.autoconfig.model.LnkInnerObject;
import com.link.base.base.autoconfig.autoconfig.service.LnkInnerObjectService;
import com.link.base.base.autoconfig.nocode.dao.mybatis.mapper.LnkInnerCodeMapper;
import com.link.base.base.autoconfig.nocode.model.LnkInnerCode;
import com.link.base.base.autoconfig.nocode.model.SourceCodeObject;
import com.link.base.base.pageurls.service.PageUrlsService;
import com.link.core.basic.dao.mapper.BasicMapper;
import com.link.core.basic.model.BasicModel;
import com.link.core.basic.query.Filter;
import com.link.core.basic.query.QueryParams;
import com.link.core.basic.service.BasicServiceException;
import com.link.core.basic.service.BasicServiceImpl;
import com.link.core.basic.service.ServiceException;
import com.link.core.util.RedisUtil;
import com.link.core.util.StringUtils;
import com.link.core.util.redisclient.LinkRedisClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * 自动配置的代码版本
 *
 * @author 何启谦
 * @date 2019/09/06 14:36
 */
@Service
public class LnkInnerCodeServiceImpl extends BasicServiceImpl<LnkInnerCode> implements LnkInnerCodeService {

    @Resource
    private LnkInnerCodeMapper lnkInnerCodeMapper;
    @Resource
    private LnkInnerObjectService lnkInnerObjectMySqlService;
    @Resource
    private NoCodeService noCodeService;
    @Resource
    private PageUrlsService pageUrlsService;

    /**
     * 缓存vue代码的key
     */
    private static final String REDIS_KEY_VUE_CODE = "base:autoconfig:nocode:vue:";

    @Override
    public BasicMapper<LnkInnerCode> getBasicMapper() {
        return lnkInnerCodeMapper;
    }

    @Transactional(rollbackFor = {BasicServiceException.class})
    @Override
    public void insert(LnkInnerCode entity) throws Exception {
        Long lnkInnerObjectId = entity.getHeadId();
        if (lnkInnerObjectId == null) {
            throw new ServiceException("lnkInnerObjectId 不允许为空");
        }
        int codeVersion = getNewVersion(lnkInnerObjectId);
        entity.setCodeVersion(codeVersion);

        LnkInnerObject lnkInnerObject = lnkInnerObjectMySqlService.queryById(lnkInnerObjectId);
        String packagePrefix = lnkInnerObject.getAttr04() + "." + lnkInnerObject.getAttr03();
        entity.setPackagePrefix(packagePrefix);

        String entityName = lnkInnerObject.getName();
        entity.setEntityName(entityName);

        // 设置对象"虚拟"路径：nocode服务的包名+对象名
        String modelPath = "com.link.nocode.base.dynamic." + entityName;
        entity.setModelPath(modelPath);

        //新建数据
        super.insert(entity);
    }

    @Override
    public void checkCode(Long id) throws Exception {
        LnkInnerCode entity = queryById(id);
        if (entity == null) {
            throw new ServiceException("找不到对象，id：" + id);
        }
        SourceCodeObject sourceCodeObject = PackageNameProcessor.createSourceCodeObject(entity);
        noCodeService.checkCode(sourceCodeObject);
    }

    @Transactional(rollbackFor = {BasicServiceException.class})
    @Override
    public void deploy(Long id) throws Exception {
        LnkInnerCode entity = queryById(id);
        if (entity == null) {
            throw new ServiceException("找不到对象，id：" + id);
        }
        lnkInnerObjectMySqlService.deployIdUpdate(entity.getHeadId(), entity.getId());
        pageUrlsService.insertUrls(entity);
        SourceCodeObject sourceCodeObject = PackageNameProcessor.createSourceCodeObject(entity);
        noCodeService.deployToAllApp(sourceCodeObject);

        // 设置缓存
        setRedisVueCode(entity.getModelPath(), entity.getVueCode());
    }

    @Transactional(rollbackFor = {BasicServiceException.class})
    @Override
    public void uninstall(Long lnkInnerObjectId) throws Exception {
        LnkInnerCode lnkInnerCode = queryDeployedByHeadId(lnkInnerObjectId);
        lnkInnerObjectMySqlService.deployIdUpdate(lnkInnerObjectId, null);
        // 删除前端vue代码的缓存
        if (lnkInnerCode != null) {
            delRedisVueCode(lnkInnerCode.getModelPath());
        }
        // 遍历nocode服务，卸载
        noCodeService.uninstallModularForUUID(String.valueOf(lnkInnerObjectId));
    }

    @Transactional(rollbackFor = {BasicServiceException.class})
    @Override
    public void insertAndDeploy(LnkInnerCode entity) throws Exception {
        insert(entity);
        deploy(entity.getId());
    }

    @Override
    public void update(LnkInnerCode entity) {
        throw new ServiceException("该对象不支持修改");
    }

    @Override
    public int getNewVersion(Long lnkInnerObjectId) {
        if (lnkInnerObjectId == null) {
            throw new ServiceException("lnkInnerObjectId 不允许为空");
        }
        LnkInnerCode entity = new LnkInnerCode();
        entity.setHeadId(lnkInnerObjectId);
        return lnkInnerCodeMapper.getNewVersion(entity);
    }

    @Override
    public LnkInnerCode generateCode(Long lnkInnerObjectId) throws Exception {
        if (lnkInnerObjectId == null) {
            throw new ServiceException("lnkInnerObjectId 不允许为空");
        }
        List<FileExport> files = lnkInnerObjectMySqlService.newObjectFile(lnkInnerObjectId);
        if (files == null || files.isEmpty()) {
            return null;
        }

        LnkInnerObject lnkInnerObject = lnkInnerObjectMySqlService.queryById(lnkInnerObjectId);
        String entityName = StringUtils.toCalmelCase(lnkInnerObject.getName());

        LnkInnerCode lnkInnerCode = new LnkInnerCode();
        for (FileExport file : files) {
            String txtContent = file.getTxtContent();
            if (StringUtils.isNull(txtContent)) {
                continue;
            }
            switch (file.getCodeType()) {
                case JAVA_CONTROLLER:
                    // 替换controller的url
                    String targetStr = "/link/" + entityName;
                    String replacement = "/nocode/" + entityName;
                    lnkInnerCode.setControllerCode(txtContent.replace(targetStr, replacement));
                    break;
                case JAVA_SERVICE:
                    lnkInnerCode.setServiceCode(txtContent);
                    break;
                case JAVA_SERVICE_IMPL:
                    lnkInnerCode.setServiceImpCode(txtContent);
                    break;
                case JAVA_MODEL:
                    lnkInnerCode.setModelCode(txtContent);
                    break;
                case JAVA_MAPPER:
                    lnkInnerCode.setMapperCode(txtContent);
                    break;
                case XML_MAPPER_MYSQL:
                    lnkInnerCode.setMysqlXmlCode(txtContent);
                    break;
                case XML_MAPPER_ORACLE:
                    lnkInnerCode.setOracleXmlCode(txtContent);
                    break;
                case HTML_LIST_VUE:
                    lnkInnerCode.setVueCode(txtContent);
                    break;
                default:
                    break;
            }
        }
        return lnkInnerCode;
    }

    @Override
    public String getDeployedVueCode(String modelPath) throws Exception {
        // 从缓存中获取代码内容
        String vueCode = getRedisVueCode(modelPath);
        if (vueCode != null) {
            return vueCode;
        }

        // 从数据库中获取代码内容
        LnkInnerCode lnkInnerCode = queryDeployedByModelPath(modelPath);
        if (lnkInnerCode == null) {
            return null;
        }
        vueCode = lnkInnerCode.getVueCode();
        // 将代码内容设置到缓存中
        setRedisVueCode(modelPath, vueCode);
        return vueCode;
    }

    /**
     * 设置前端vue代码缓存
     *
     * @param modelPath 对象路径
     * @param value     代码内容
     */
    private void setRedisVueCode(String modelPath, String value) {
        String redisKey = REDIS_KEY_VUE_CODE + modelPath;
        LinkRedisClient jedis = null;
        try {
            jedis = RedisUtil.getJedis();
            jedis.set(redisKey, value);
            jedis.expire(redisKey, 604800);
        } finally {
            RedisUtil.returnResource(jedis);
        }
    }

    /**
     * 从缓存里获取前端vue代码
     *
     * @param modelPath 对象路径
     */
    private String getRedisVueCode(String modelPath) {
        String redisKey = REDIS_KEY_VUE_CODE + modelPath;
        String vueCode;
        LinkRedisClient jedis = null;
        try {
            jedis = RedisUtil.getJedis();
            vueCode = jedis.get(redisKey);
        } finally {
            RedisUtil.returnResource(jedis);
        }
        return vueCode;
    }

    /**
     * 从缓存里删除前端vue代码
     *
     * @param modelPath 对象路径
     */
    private void delRedisVueCode(String modelPath) {
        String redisKey = REDIS_KEY_VUE_CODE + modelPath;
        LinkRedisClient jedis = null;
        try {
            jedis = RedisUtil.getJedis();
            jedis.del(redisKey);
        } finally {
            RedisUtil.returnResource(jedis);
        }
    }

    @Override
    public LnkInnerCode queryDeployedByModelPath(String modelPath) throws Exception {
        if (StringUtils.isNull(modelPath)) {
            throw new BasicServiceException("modelPath不允许为空");
        }
        QueryParams qps = new QueryParams();
        qps.setPageFlag(true);
        qps.setTotalFlag(false);
        qps.setPage(1);
        qps.setRows(2);
        qps.addFilter(new Filter("modelPath", Filter.OP_EQUAL, modelPath));
        qps.addFilter(new Filter("deployFlag", Filter.OP_EQUAL, "Y"));
        LnkInnerCode temp = BasicModel.transformClass(LnkInnerCode.class, qps);
        List<LnkInnerCode> list = queryByExamplePage(temp);
        if (list == null || list.isEmpty()) {
            return null;
        }
        if (list.size() > 1) {
            throw new BasicServiceException("找到多个版本代码");
        }
        Long id = list.get(0).getId();
        return queryById(id);
    }

    @Override
    public LnkInnerCode queryDeployedByHeadId(Long lnkInnerObjectId) throws Exception {
        LnkInnerObject lnkInnerObject = lnkInnerObjectMySqlService.queryById(lnkInnerObjectId);
        if (lnkInnerObject == null) {
            throw new BasicServiceException("找不到LnkInnerObject，lnkInnerObjectId：" + lnkInnerObjectId);
        }
        Long deployId = lnkInnerObject.getDeployId();
        if (deployId == null) {
            return null;
        }
        return queryById(deployId);
    }

    @Override
    public LnkInnerCode queryById(LnkInnerCode entity) throws Exception {
        LnkInnerCode lnkInnerCode = super.queryById(entity);
        if (lnkInnerCode == null) {
            return null;
        }
        setCodeContentById(lnkInnerCode.getId(), lnkInnerCode);
        return lnkInnerCode;
    }

    @Override
    public void setCodeContentById(Long id, LnkInnerCode lnkInnerCode) {
        if (id == null) {
            throw new BasicServiceException("id不允许为空");
        }
        if (lnkInnerCode == null) {
            throw new BasicServiceException("参数lnkInnerCode不允许为空");
        }
        LnkInnerCode example = new LnkInnerCode();
        example.setId(id);
        LnkInnerCode temp = lnkInnerCodeMapper.queryCodeContentById(example);
        if (temp == null) {
            throw new BasicServiceException("找不到LnkInnerCode，id：" + id);
        }
        lnkInnerCode.setControllerCode(temp.getControllerCode());
        lnkInnerCode.setServiceCode(temp.getServiceCode());
        lnkInnerCode.setServiceImpCode(temp.getServiceImpCode());
        lnkInnerCode.setModelCode(temp.getModelCode());
        lnkInnerCode.setMapperCode(temp.getMapperCode());
        lnkInnerCode.setMysqlXmlCode(temp.getMysqlXmlCode());
        lnkInnerCode.setOracleXmlCode(temp.getOracleXmlCode());
        lnkInnerCode.setVueCode(temp.getVueCode());
    }

    @Override
    public List<LnkInnerCode> queryAllPlanDeploy() throws Exception {
        int maxSize = 1000;
        QueryParams qps = new QueryParams();
        qps.setPageFlag(true);
        qps.setTotalFlag(false);
        qps.setPage(1);
        qps.setRows(maxSize);
        qps.addFilter(new Filter("deployFlag", Filter.OP_EQUAL, "Y"));
        LnkInnerCode temp = BasicModel.transformClass(LnkInnerCode.class, qps);
        List<LnkInnerCode> list = queryByExamplePage(temp);
        if (list == null) {
            list = new ArrayList<>();
        }
        if (list.size() >= (maxSize + 1)) {
            throw new RuntimeException("待部署的模块超过启动时的最大限制" + maxSize);
        }
        // 填充代码内容
        for (LnkInnerCode item : list) {
            setCodeContentById(item.getId(), item);
        }

        return list;
    }
}

